package com.kevin.v2.spring.formework.context;

import com.kevin.v1.springframework.annotation.KvAutowired;
import com.kevin.v1.springframework.annotation.KvController;
import com.kevin.v1.springframework.annotation.KvService;
import com.kevin.v2.spring.formework.aop.config.KvConfig;
import com.kevin.v2.spring.formework.aop.proxy.KvAopProxy;
import com.kevin.v2.spring.formework.aop.proxy.KvCglibAopProxy;
import com.kevin.v2.spring.formework.aop.proxy.KvJDKAopProxy;
import com.kevin.v2.spring.formework.aop.support.KvAdviseSupport;
import com.kevin.v2.spring.formework.beans.KvBeanWapper;
import com.kevin.v2.spring.formework.beans.config.KvBeanDefinition;
import com.kevin.v2.spring.formework.context.support.KvBeanDefinitionReader;
import com.kevin.v2.spring.formework.context.support.KvDefaultListableBeanFactory;
import com.kevin.v2.spring.formework.core.KvBeanFactory;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 容器入口
 */
@Slf4j
public class KvApplicationContext extends KvDefaultListableBeanFactory implements KvBeanFactory {


   //需要加载的资源文件
   private String[] configLocations;

   //解析配置文件对象
   private KvBeanDefinitionReader definitionReader;

   //用于缓存单例对象
   private Map<String, Object> singletonCacheMap = new HashMap<>();

   //保存ioc 初始化完成后的bean 的实例
   private Map<String, KvBeanWapper> beanWapperMap = new HashMap<>();


   public KvApplicationContext (String... configLocations) {
      this.configLocations = configLocations;
      try {
         refresh();
      } catch (Exception e) {
         log.error(Arrays.toString(e.getStackTrace()));
         e.printStackTrace();

      }
   }


   /**
    * 容器初始化
    *
    * @throws Exception
    */
   @Override
   public void refresh () throws Exception {

      //1.定位配置文件
      definitionReader = new KvBeanDefinitionReader(this.configLocations);
      //2.加载并解析配置文件
      List<KvBeanDefinition> beanDefinitionList = definitionReader.loadBeanDefinitions();
      //3.注册到ioc容器中
      doRegisterBeanDefinition(beanDefinitionList);
      //4.非 lazyInit=true 的bean 提前初始化和依赖注入
      doAutowrited();


   }

   private void doAutowrited () {
      if (super.beanDefinitionMap.isEmpty()) {
         return;
      }
      for (Map.Entry<String, KvBeanDefinition> beanDefinition : beanDefinitionMap.entrySet()) {
         if (beanDefinition.getValue().isLazyInit()) continue;
         //初始化bean
         getBean(beanDefinition.getKey());
      }
   }

   private void doRegisterBeanDefinition (List<KvBeanDefinition> beanDefinitionList) throws Exception {
      for (KvBeanDefinition kvBeanDefinition : beanDefinitionList) {
         if (super.beanDefinitionMap.get(kvBeanDefinition.getFactoryName()) != null) throw new Exception("the " + kvBeanDefinition.getFactoryName() + " exist!!");

         super.beanDefinitionMap.put(kvBeanDefinition.getFactoryName(), kvBeanDefinition);
      }
   }

   /**
    * 依赖注入开始
    *
    * @param beanName
    * @return
    */
   @Override
   public Object getBean (String beanName) {

      KvBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

      Object instance = initialBean(beanName,beanDefinition);
      if (instance == null) {
         return null;
      }

      KvBeanWapper beanWapper = new KvBeanWapper(instance);

      beanWapperMap.put(beanDefinition.getFactoryName(), beanWapper);
      beanWapperMap.put(beanDefinition.getBeanClassName(), beanWapper);

      populateBean(beanName, instance);

      return beanWapperMap.get(beanName).getWapperInstance();
   }


   /**
    * 初始化bean
    *
    *
    * @param beanName
    * @param beanDefinition
    * @return
    */
   private Object initialBean (String beanName, KvBeanDefinition beanDefinition) {
      Object instance = null;
      try {
         if (singletonCacheMap.containsKey(beanDefinition.getBeanClassName())) {
            instance = singletonCacheMap.get(beanDefinition.getBeanClassName());
         } else {
            Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
            instance = clazz.newInstance();

            //初始化aop 配置
            KvAdviseSupport kvAdviseSupport = instantionAopConfig();
            kvAdviseSupport.setTargetClass(clazz);
            kvAdviseSupport.setTarget(instance);
            //判断该类是否需要被aop代理
            if (kvAdviseSupport.pointCutMatch()) {

               KvAopProxy proxy = getAopProxy(kvAdviseSupport);
               instance = proxy.getProxy();
            }

            singletonCacheMap.put(beanDefinition.getBeanClassName(), instance);
            singletonCacheMap.put(beanDefinition.getFactoryName(), instance);
         }
      } catch (ClassNotFoundException e) {
         e.printStackTrace();
      } catch (IllegalAccessException e) {
         e.printStackTrace();
      } catch (InstantiationException e) {
         e.printStackTrace();
      }
      return instance;
   }

   private KvAdviseSupport instantionAopConfig () {
      KvConfig config = new KvConfig();
      config.setAspectBefore(getProperties().getProperty("aspectBefore"));
      config.setAspectAfter(getProperties().getProperty("aspectAfter"));
      config.setAspectAfterThrow(getProperties().getProperty("aspectAfterThrow"));
      config.setAspectAfterThrowingName(getProperties().getProperty("aspectAfterThrowingName"));
      config.setAspectClass(getProperties().getProperty("aspectClass"));
      config.setPointCut(getProperties().getProperty("pointCut"));
      KvAdviseSupport support = new KvAdviseSupport(config);
      return support;
   }

   /**
    * 选择代理策略
    * JDK or CGLIB
    * @param support
    * @return
    */
   private KvAopProxy getAopProxy (KvAdviseSupport support) {
      if (support.getTargetClass().getInterfaces().length > 0) {
         return new KvJDKAopProxy(support);
      }
      return new KvCglibAopProxy(support);


   }

   /**
    * 执行依赖注入
    *
    * @param beanName
    * @param instance
    */
   public void populateBean (String beanName, Object instance) {
      Class<?> clazz = instance.getClass();
      if (clazz.isAnnotationPresent(KvController.class) || clazz.isAnnotationPresent(KvService.class)) {
         //拿到改clazz 所有属性
         Field[] fields = clazz.getDeclaredFields();
         for (Field field : fields) {

            if (field.isAnnotationPresent(KvAutowired.class)) {
               KvAutowired autowired = field.getAnnotation(KvAutowired.class);
               String aotowiredBeanName = autowired.value().trim();
               if ("".equals(aotowiredBeanName)) {
                aotowiredBeanName = field.getType().getName();
               }
               field.setAccessible(true);
               try {
                  if (beanWapperMap.get(aotowiredBeanName) == null) continue;
                  field.set(instance, beanWapperMap.get(aotowiredBeanName).getWapperInstance());
               } catch (IllegalAccessException e) {
                  e.printStackTrace();
               }
            }
         }
      }
   }

   public Properties getProperties () {
      return definitionReader.getProperties();
   }




}
